home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / NDK / NDK_3.5 / Examples / AsynchIO / asyncio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-30  |  21.7 KB  |  686 lines

  1. /*
  2.  * COPYRIGHT:
  3.  *
  4.  *   Unless otherwise noted, all files are Copyright (c) 1999 Amiga, Inc.
  5.  *   All rights reserved.
  6.  *
  7.  * DISCLAIMER:
  8.  *
  9.  *   This software is provided "as is". No representations or warranties
  10.  *   are made with respect to the accuracy, reliability, performance,
  11.  *   currentness, or operation of this software, and all use is at your
  12.  *   own risk. Neither Amiga nor the authors assume any responsibility
  13.  *   or liability whatsoever with respect to your use of this software.
  14.  */
  15.  
  16. #include <exec/types.h>
  17. #include <exec/memory.h>
  18. #include <dos/dos.h>
  19. #include <dos/dosextens.h>
  20.  
  21. #include <clib/exec_protos.h>
  22. #include <clib/dos_protos.h>
  23.  
  24. #include <pragmas/exec_pragmas.h>
  25. #include <pragmas/dos_pragmas.h>
  26.  
  27. #include "asyncio.h"
  28.  
  29.  
  30. /*****************************************************************************/
  31.  
  32.  
  33. extern struct Library *DOSBase;
  34. extern struct Library *SysBase;
  35.  
  36.  
  37. /*****************************************************************************/
  38.  
  39.  
  40. /* this macro lets us long-align structures on the stack */
  41. #define D_S(type,name) char a_##name[sizeof(type)+3]; \
  42.                        type *name = (type *)((LONG)(a_##name+3) & ~3);
  43.  
  44.  
  45. /*****************************************************************************/
  46.  
  47.  
  48. /* send out an async packet to the file system. */
  49. static void SendPacket(AsyncFile *file, APTR arg2)
  50. {
  51.     file->af_Packet.sp_Pkt.dp_Port = &file->af_PacketPort;
  52.     file->af_Packet.sp_Pkt.dp_Arg2 = (LONG)arg2;
  53.     PutMsg(file->af_Handler, &file->af_Packet.sp_Msg);
  54.     file->af_PacketPending = TRUE;
  55. }
  56.  
  57.  
  58. /*****************************************************************************/
  59.  
  60.  
  61. /* this function waits for a packet to come back from the file system. If no
  62.  * packet is pending, state from the previous packet is returned. This ensures
  63.  * that once an error occurs, it state is maintained for the rest of the life
  64.  * of the file handle.
  65.  *
  66.  * This function also deals with IO errors, bringing up the needed DOS
  67.  * requesters to let the user retry an operation or cancel it.
  68.  */
  69. static LONG WaitPacket(AsyncFile *file)
  70. {
  71. LONG bytes;
  72.  
  73.     if (file->af_PacketPending)
  74.     {
  75.         while (TRUE)
  76.         {
  77.             /* This enables signalling when a packet comes back to the port */
  78.             file->af_PacketPort.mp_Flags = PA_SIGNAL;
  79.  
  80.             /* Wait for the packet to come back, and remove it from the message
  81.              * list. Since we know no other packets can come in to the port, we can
  82.              * safely use Remove() instead of GetMsg(). If other packets could come in,
  83.              * we would have to use GetMsg(), which correctly arbitrates access in such
  84.              * a case
  85.              */
  86.             Remove((struct Node *)WaitPort(&file->af_PacketPort));
  87.  
  88.             /* set the port type back to PA_IGNORE so we won't be bothered with
  89.              * spurious signals
  90.              */
  91.             file->af_PacketPort.mp_Flags = PA_IGNORE;
  92.  
  93.             /* mark packet as no longer pending since we removed it */
  94.             file->af_PacketPending = FALSE;
  95.  
  96.             bytes = file->af_Packet.sp_Pkt.dp_Res1;
  97.             if (bytes >= 0)
  98.             {
  99.                 /* packet didn't report an error, so bye... */
  100.                 return(bytes);
  101.             }
  102.  
  103.             /* see if the user wants to try again... */
  104.             if (ErrorReport(file->af_Packet.sp_Pkt.dp_Res2,REPORT_STREAM,file->af_File,NULL))
  105.                 return(-1);
  106.  
  107.             /* user wants to try again, resend the packet */
  108.             if (file->af_ReadMode)
  109.                 SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  110.             else
  111.                 SendPacket(file,file->af_Buffers[1 - file->af_CurrentBuf]);
  112.          }
  113.     }
  114.  
  115.     /* last packet's error code, or 0 if packet was never sent */
  116.     SetIoErr(file->af_Packet.sp_Pkt.dp_Res2);
  117.  
  118.     return(file->af_Packet.sp_Pkt.dp_Res1);
  119. }
  120.  
  121.  
  122. /*****************************************************************************/
  123.  
  124.  
  125. /* this function puts the packet back on the message list of our
  126.  * message port.
  127.  */
  128. static void RequeuePacket(AsyncFile *file)
  129. {
  130.     AddHead(&file->af_PacketPort.mp_MsgList,&file->af_Packet.sp_Msg.mn_Node);
  131.     file->af_PacketPending = TRUE;
  132. }
  133.  
  134.  
  135. /*****************************************************************************/
  136.  
  137.  
  138. /* this function records a failure from a synchronous DOS call into the
  139.  * packet so that it gets picked up by the other IO routines in this module
  140.  */
  141. static void RecordSyncFailure(AsyncFile *file)
  142. {
  143.     file->af_Packet.sp_Pkt.dp_Res1 = -1;
  144.     file->af_Packet.sp_Pkt.dp_Res2 = IoErr();
  145.     file->af_BytesLeft = 0;
  146. }
  147.  
  148.  
  149. /*****************************************************************************/
  150.  
  151.  
  152. AsyncFile *OpenAsync(const STRPTR fileName, OpenModes mode, LONG bufferSize)
  153. {
  154. AsyncFile         *file;
  155. struct FileHandle *fh;
  156. BPTR               handle;
  157. BPTR               lock;
  158. LONG               blockSize;
  159. D_S(struct InfoData,infoData);
  160.  
  161.     handle = NULL;
  162.     file   = NULL;
  163.     lock   = NULL;
  164.  
  165.     if (mode == MODE_READ)
  166.     {
  167.         if (handle = Open(fileName,MODE_OLDFILE))
  168.             lock = Lock(fileName,ACCESS_READ);
  169.     }
  170.     else
  171.     {
  172.         if (mode == MODE_WRITE)
  173.         {
  174.             handle = Open(fileName,MODE_NEWFILE);
  175.         }
  176.         else if (mode == MODE_APPEND)
  177.         {
  178.             /* in append mode, we open for writing, and then seek to the
  179.              * end of the file. That way, the initial write will happen at
  180.              * the end of the file, thus extending it
  181.              */
  182.  
  183.             if (handle = Open(fileName,MODE_READWRITE))
  184.             {
  185.                 if (Seek(handle,0,OFFSET_END) < 0)
  186.                 {
  187.                     Close(handle);
  188.                     handle = NULL;
  189.                 }
  190.             }
  191.         }
  192.  
  193.         /* we want a lock on the same device as where the file is. We can't
  194.          * use DupLockFromFH() for a write-mode file though. So we get sneaky
  195.          * and get a lock on the parent of the file
  196.          */
  197.         if (handle)
  198.             lock = ParentOfFH(handle);
  199.     }
  200.  
  201.     if (handle)
  202.     {
  203.         /* if it was possible to obtain a lock on the same device as the
  204.          * file we're working on, get the block size of that device and
  205.          * round up our buffer size to be a multiple of the block size.
  206.          * This maximizes DMA efficiency.
  207.          */
  208.  
  209.         blockSize = 512;
  210.         if (lock)
  211.         {
  212.             if (Info(lock,infoData))
  213.             {
  214.                 blockSize  = infoData->id_BytesPerBlock;
  215.                 bufferSize = (((bufferSize + (blockSize*2) - 1) / (blockSize*2)) * (blockSize*2));
  216.             }
  217.             UnLock(lock);
  218.             lock = NULL;
  219.         }
  220.  
  221.         /* now allocate the ASyncFile structure, as well as the read buffers.
  222.          * Add 15 bytes to the total size in order to allow for later
  223.          * quad-longword alignment of the buffers
  224.          */
  225.  
  226.         if (file = AllocVec(sizeof(AsyncFile) + bufferSize + 15,MEMF_PUBLIC | MEMF_ANY))
  227.         {
  228.             file->af_File      = handle;
  229.             file->af_ReadMode  = (mode == MODE_READ);
  230.             file->af_BlockSize = blockSize;
  231.  
  232.             /* initialize the ASyncFile structure. We do as much as we can here,
  233.              * in order to avoid doing it in more critical sections
  234.              *
  235.              * Note how the two buffers used are quad-longword aligned. This
  236.              * helps performance on 68040 systems with copyback cache. Aligning
  237.              * the data avoids a nasty side-effect of the 040 caches on DMA.
  238.              * Not aligning the data causes the device driver to have to do
  239.              * some magic to avoid the cache problem. This magic will generally
  240.              * involve flushing the CPU caches. This is very costly on an 040.
  241.              * Aligning things avoids the need for magic, at the cost of at
  242.              * most 15 bytes of ram.
  243.              */
  244.  
  245.             fh                     = BADDR(file->af_File);
  246.             file->af_Handler       = fh->fh_Type;
  247.             file->af_BufferSize    = bufferSize / 2;
  248.             file->af_Buffers[0]    = (APTR)(((ULONG)file + sizeof(AsyncFile) + 15) & 0xfffffff0);
  249.             file->af_Buffers[1]    = (APTR)((ULONG)file->af_Buffers[0] + file->af_BufferSize);
  250.             file->af_Offset        = file->af_Buffers[0];
  251.             file->af_CurrentBuf    = 0;
  252.             file->af_SeekOffset    = 0;
  253.             file->af_PacketPending = FALSE;
  254.  
  255.             /* this is the port used to get the packets we send out back.
  256.              * It is initialized to PA_IGNORE, which means that no signal is
  257.              * generated when a message comes in to the port. The signal bit
  258.              * number is initialized to SIGB_SINGLE, which is the special bit
  259.              * that can be used for one-shot signalling. The signal will never
  260.              * be set, since the port is of type PA_IGNORE. We'll change the
  261.              * type of the port later on to PA_SIGNAL whenever we need to wait
  262.              * for a message to come in.
  263.              *
  264.              * The trick used here avoids the need to allocate an extra signal
  265.              * bit for the port. It is quite efficient.
  266.              */
  267.  
  268.             file->af_PacketPort.mp_MsgList.lh_Head     = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Tail;
  269.             file->af_PacketPort.mp_MsgList.lh_Tail     = NULL;
  270.             file->af_PacketPort.mp_MsgList.lh_TailPred = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Head;
  271.             file->af_PacketPort.mp_Node.ln_Type        = NT_MSGPORT;
  272.             file->af_PacketPort.mp_Node.ln_Name        = NULL;
  273.             file->af_PacketPort.mp_Flags               = PA_IGNORE;
  274.             file->af_PacketPort.mp_SigBit              = SIGB_SINGLE;
  275.             file->af_PacketPort.mp_SigTask             = FindTask(NULL);
  276.  
  277.             file->af_Packet.sp_Pkt.dp_Link          = &file->af_Packet.sp_Msg;
  278.             file->af_Packet.sp_Pkt.dp_Arg1          = fh->fh_Arg1;
  279.             file->af_Packet.sp_Pkt.dp_Arg3          = file->af_BufferSize;
  280.             file->af_Packet.sp_Pkt.dp_Res1          = 0;
  281.             file->af_Packet.sp_Pkt.dp_Res2          = 0;
  282.             file->af_Packet.sp_Msg.mn_Node.ln_Name  = (STRPTR)&file->af_Packet.sp_Pkt;
  283.             file->af_Packet.sp_Msg.mn_Node.ln_Type  = NT_MESSAGE;
  284.             file->af_Packet.sp_Msg.mn_Length        = sizeof(struct StandardPacket);
  285.  
  286.             if (mode == MODE_READ)
  287.             {
  288.                 /* if we are in read mode, send out the first read packet to
  289.                  * the file system. While the application is getting ready to
  290.                  * read data, the file system will happily fill in this buffer
  291.                  * with DMA transfers, so that by the time the application
  292.                  * needs the data, it will be in the buffer waiting
  293.                  */
  294.  
  295.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_READ;
  296.                 file->af_BytesLeft             = 0;
  297.                 file->af_Offset                = file->af_Buffers[1];
  298.                 if (file->af_Handler)
  299.                     SendPacket(file,file->af_Buffers[0]);
  300.             }
  301.             else
  302.             {
  303.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_WRITE;
  304.                 file->af_BytesLeft             = file->af_BufferSize;
  305.             }
  306.         }
  307.         else
  308.         {
  309.             Close(handle);
  310.  
  311.             SetIoErr(ERROR_NO_FREE_STORE);
  312.         }
  313.     }
  314.  
  315.     if(lock != NULL)
  316.     {
  317.         LONG error = IoErr();
  318.  
  319.         UnLock(lock);
  320.  
  321.         SetIoErr(error);
  322.     }
  323.  
  324.     return(file);
  325. }
  326.  
  327.  
  328. /*****************************************************************************/
  329.  
  330.  
  331. LONG CloseAsync(AsyncFile *file)
  332. {
  333. LONG result;
  334.  
  335.     if (file)
  336.     {
  337.         result = WaitPacket(file);
  338.         if (result >= 0)
  339.         {
  340.             if (!file->af_ReadMode)
  341.             {
  342.                 /* this will flush out any pending data in the write buffer */
  343.                 if (file->af_BufferSize > file->af_BytesLeft)
  344.                     result = Write(file->af_File,file->af_Buffers[file->af_CurrentBuf],file->af_BufferSize - file->af_BytesLeft);
  345.             }
  346.         }
  347.  
  348.         Close(file->af_File);
  349.         FreeVec(file);
  350.     }
  351.     else
  352.     {
  353.         SetIoErr(ERROR_INVALID_LOCK);
  354.         result = -1;
  355.     }
  356.  
  357.     return(result);
  358. }
  359.  
  360.  
  361. /*****************************************************************************/
  362.  
  363.  
  364. LONG ReadAsync(AsyncFile *file, APTR buffer, LONG numBytes)
  365. {
  366. LONG totalBytes;
  367. LONG bytesArrived;
  368.  
  369.     totalBytes = 0;
  370.  
  371.     /* if we need more bytes than there are in the current buffer, enter the
  372.      * read loop
  373.      */
  374.  
  375.     while (numBytes > file->af_BytesLeft)
  376.     {
  377.         /* drain buffer */
  378.         CopyMem(file->af_Offset,buffer,file->af_BytesLeft);
  379.  
  380.         numBytes           -= file->af_BytesLeft;
  381.         buffer              = (APTR)((ULONG)buffer + file->af_BytesLeft);
  382.         totalBytes         += file->af_BytesLeft;
  383.         file->af_BytesLeft  = 0;
  384.  
  385.         bytesArrived = WaitPacket(file);
  386.         if (bytesArrived <= 0)
  387.         {
  388.             if (bytesArrived == 0)
  389.             {
  390.                 /* end of file reached */
  391.                 SetIoErr(0);
  392.  
  393.                 return(totalBytes);
  394.             }
  395.  
  396.             return(-1);
  397.         }
  398.  
  399.         /* ask that the buffer be filled */
  400.         SendPacket(file,file->af_Buffers[1-file->af_CurrentBuf]);
  401.  
  402.         if (file->af_SeekOffset > bytesArrived)
  403.             file->af_SeekOffset = bytesArrived;
  404.  
  405.         file->af_Offset      = (APTR)((ULONG)file->af_Buffers[file->af_CurrentBuf] + file->af_SeekOffset);
  406.         file->af_CurrentBuf  = 1 - file->af_CurrentBuf;
  407.         file->af_BytesLeft   = bytesArrived - file->af_SeekOffset;
  408.         file->af_SeekOffset  = 0;
  409.     }
  410.  
  411.     CopyMem(file->af_Offset,buffer,numBytes);
  412.     file->af_BytesLeft -= numBytes;
  413.     file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  414.  
  415.     return (totalBytes + numBytes);
  416. }
  417.  
  418.  
  419. /*****************************************************************************/
  420.  
  421.  
  422. LONG ReadCharAsync(AsyncFile *file)
  423. {
  424. unsigned char ch;
  425.  
  426.     if (file->af_BytesLeft)
  427.     {
  428.         /* if there is at least a byte left in the current buffer, get it
  429.          * directly. Also update all counters
  430.          */
  431.  
  432.         ch = *(char *)file->af_Offset;
  433.         file->af_BytesLeft--;
  434.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  435.  
  436.         return((LONG)ch);
  437.     }
  438.  
  439.     /* there were no characters in the current buffer, so call the main read
  440.      * routine. This has the effect of sending a request to the file system to
  441.      * have the current buffer refilled. After that request is done, the
  442.      * character is extracted for the alternate buffer, which at that point
  443.      * becomes the "current" buffer
  444.      */
  445.  
  446.     if (ReadAsync(file,&ch,1) > 0)
  447.         return((LONG)ch);
  448.  
  449.     /* We couldn't read above, so fail */
  450.  
  451.     return(-1);
  452. }
  453.  
  454.  
  455. /*****************************************************************************/
  456.  
  457.  
  458. LONG WriteAsync(AsyncFile *file, APTR buffer, LONG numBytes)
  459. {
  460. LONG totalBytes;
  461.  
  462.     totalBytes = 0;
  463.  
  464.     while (numBytes > file->af_BytesLeft)
  465.     {
  466.         /* this takes care of NIL: */
  467.         if (!file->af_Handler)
  468.         {
  469.             file->af_Offset    = file->af_Buffers[0];
  470.             file->af_BytesLeft = file->af_BufferSize;
  471.             return(numBytes);
  472.         }
  473.  
  474.         if (file->af_BytesLeft)
  475.         {
  476.             CopyMem(buffer,file->af_Offset,file->af_BytesLeft);
  477.  
  478.             numBytes   -= file->af_BytesLeft;
  479.             buffer      = (APTR)((ULONG)buffer + file->af_BytesLeft);
  480.             totalBytes += file->af_BytesLeft;
  481.         }
  482.  
  483.         if (WaitPacket(file) < 0)
  484.             return(-1);
  485.  
  486.         /* send the current buffer out to disk */
  487.         SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  488.  
  489.         file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  490.         file->af_Offset     = file->af_Buffers[file->af_CurrentBuf];
  491.         file->af_BytesLeft  = file->af_BufferSize;
  492.     }
  493.  
  494.     CopyMem(buffer,file->af_Offset,numBytes);
  495.     file->af_BytesLeft -= numBytes;
  496.     file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  497.  
  498.     return (totalBytes + numBytes);
  499. }
  500.  
  501.  
  502. /*****************************************************************************/
  503.  
  504.  
  505. LONG WriteCharAsync(AsyncFile *file, UBYTE ch)
  506. {
  507.     if (file->af_BytesLeft)
  508.     {
  509.         /* if there's any room left in the current buffer, directly write
  510.          * the byte into it, updating counters and stuff.
  511.          */
  512.  
  513.         *(UBYTE *)file->af_Offset = ch;
  514.         file->af_BytesLeft--;
  515.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  516.  
  517.         /* one byte written */
  518.         return(1);
  519.     }
  520.  
  521.     /* there was no room in the current buffer, so call the main write
  522.      * routine. This will effectively send the current buffer out to disk,
  523.      * wait for the other buffer to come back, and then put the byte into
  524.      * it.
  525.      */
  526.  
  527.     return(WriteAsync(file,&ch,1));
  528. }
  529.  
  530.  
  531. /*****************************************************************************/
  532.  
  533.  
  534. LONG SeekAsync(AsyncFile *file, LONG position, SeekModes mode)
  535. {
  536. LONG  current, target;
  537. LONG  minBuf, maxBuf;
  538. LONG  bytesArrived;
  539. LONG  diff;
  540. LONG  filePos;
  541. LONG  roundTarget;
  542. D_S(struct FileInfoBlock,fib);
  543.  
  544.     bytesArrived = WaitPacket(file);
  545.  
  546.     if (bytesArrived < 0)
  547.         return(-1);
  548.  
  549.     if (file->af_ReadMode)
  550.     {
  551.         /* figure out what the actual file position is */
  552.         filePos = Seek(file->af_File,0,OFFSET_CURRENT);
  553.         if (filePos < 0)
  554.         {
  555.             RecordSyncFailure(file);
  556.             return(-1);
  557.         }
  558.  
  559.         /* figure out what the caller's file position is */
  560.         current = filePos - (file->af_BytesLeft+bytesArrived) + file->af_SeekOffset;
  561.  
  562.         /* figure out the absolute offset within the file where we must seek to */
  563.         if (mode == MODE_CURRENT)
  564.         {
  565.             target = current + position;
  566.         }
  567.         else if (mode == MODE_START)
  568.         {
  569.             target = position;
  570.         }
  571.         else /* if (mode == MODE_END) */
  572.         {
  573.             if (!ExamineFH(file->af_File,fib))
  574.             {
  575.                 RecordSyncFailure(file);
  576.                 return(-1);
  577.             }
  578.  
  579.             target = fib->fib_Size + position;
  580.         }
  581.  
  582.         /* figure out what range of the file is currently in our buffers */
  583.         minBuf = current - (LONG)((ULONG)file->af_Offset - (ULONG)file->af_Buffers[1-file->af_CurrentBuf]);
  584.         maxBuf = current + file->af_BytesLeft + bytesArrived;  /* WARNING: this is one too big */
  585.  
  586.         diff = target - current;
  587.  
  588.         if ((target < minBuf) || (target >= maxBuf))
  589.         {
  590.             /* the target seek location isn't currently in our buffers, so
  591.              * move the actual file pointer to the desired location, and then
  592.              * restart the async read thing...
  593.              */
  594.  
  595.             /* this is to keep our file reading block-aligned on the device.
  596.              * block-aligned reads are generally quite a bit faster, so it is
  597.              * worth the trouble to keep things aligned
  598.              */
  599.             roundTarget = (target / file->af_BlockSize) * file->af_BlockSize;
  600.  
  601.             if (Seek(file->af_File,roundTarget-filePos,OFFSET_CURRENT) < 0)
  602.             {
  603.                 RecordSyncFailure(file);
  604.                 return(-1);
  605.             }
  606.  
  607.             SendPacket(file,file->af_Buffers[0]);
  608.  
  609.             file->af_SeekOffset = target-roundTarget;
  610.             file->af_BytesLeft  = 0;
  611.             file->af_CurrentBuf = 0;
  612.             file->af_Offset     = file->af_Buffers[1];
  613.         }
  614.         else if ((target < current) || (diff <= file->af_BytesLeft))
  615.         {
  616.             /* one of the two following things is true:
  617.              *
  618.              * 1. The target seek location is within the current read buffer,
  619.              * but before the current location within the buffer. Move back
  620.              * within the buffer and pretend we never got the pending packet,
  621.              * just to make life easier, and faster, in the read routine.
  622.              *
  623.              * 2. The target seek location is ahead within the current
  624.              * read buffer. Advance to that location. As above, pretend to
  625.              * have never received the pending packet.
  626.              */
  627.  
  628.             RequeuePacket(file);
  629.  
  630.             file->af_BytesLeft -= diff;
  631.             file->af_Offset     = (APTR)((ULONG)file->af_Offset + diff);
  632.         }
  633.         else
  634.         {
  635.             /* at this point, we know the target seek location is within
  636.              * the buffer filled in by the packet that we just received
  637.              * at the start of this function. Throw away all the bytes in the
  638.              * current buffer, send a packet out to get the async thing going
  639.              * again, readjust buffer pointers to the seek location, and return
  640.              * with a grin on your face... :-)
  641.              */
  642.  
  643.             SendPacket(file,file->af_Buffers[1-file->af_CurrentBuf]);
  644.  
  645.             diff -= file->af_BytesLeft - file->af_SeekOffset;
  646.  
  647.             file->af_Offset     = (APTR)((ULONG)file->af_Buffers[file->af_CurrentBuf] + diff);
  648.             file->af_BytesLeft  = bytesArrived - diff;
  649.             file->af_SeekOffset = 0;
  650.             file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  651.         }
  652.     }
  653.     else
  654.     {
  655.         if (file->af_BufferSize > file->af_BytesLeft)
  656.         {
  657.             if (Write(file->af_File,file->af_Buffers[file->af_CurrentBuf],file->af_BufferSize - file->af_BytesLeft) < 0)
  658.             {
  659.                 RecordSyncFailure(file);
  660.                 return(-1);
  661.             }
  662.         }
  663.  
  664.         /* this will unfortunately generally result in non block-aligned file
  665.          * access. We could be sneaky and try to resync our file pos at a
  666.          * later time, but we won't bother. Seeking in write-only files is
  667.          * relatively rare (except when writing IFF files with unknown chunk
  668.          * sizes, where the chunk size has to be written after the chunk data)
  669.          */
  670.  
  671.         current = Seek(file->af_File,position,mode);
  672.  
  673.         if (current < 0)
  674.         {
  675.             RecordSyncFailure(file);
  676.             return(-1);
  677.         }
  678.  
  679.         file->af_BytesLeft  = file->af_BufferSize;
  680.         file->af_CurrentBuf = 0;
  681.         file->af_Offset     = file->af_Buffers[0];
  682.     }
  683.  
  684.     return(current);
  685. }
  686.